﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Serialization;

namespace PolarSonicLink
{
    public class DecoderHelpers
    {
        public static DateTime DecodeDateTime(int msb, int lsb, int BCDhour, int BCDminute, int BCDsecond)
        {
            int s = ( msb << 8 ) + lsb;
            DateTime returnDate = new DateTime(2000 + ( s >> 9 ),( s & 0x1E0 ) >> 5,  s & 0x1F, 
                BCDToDecimal(BCDhour), BCDToDecimal(BCDminute), BCDToDecimal(BCDsecond));
            return ( returnDate );
        }

        public static int BCDToDecimal(int bcd)
        {
            return ( ( bcd >> 4 ) * 10 + ( bcd & 0x0F ) );
        }

        private static int TimeToHours(int hours)
        {
            // rs200 marks the first bit as one if time data comes from autolap
            // as maximum exercise time is 100 hours, it i safe to assume that this first bit never
            // stores any other information. Here, information about autolap is simply
            // discarded
            hours = hours & 0x7F;
            return hours;
        }

        private static int TimeToMinutes(int minutes)
        {
            return ( minutes & 0x3F );
        }

        private static int TimeToSeconds(int seconds)
        {
            return ( seconds & 0x3F );
        }

        private static int TimeToTenths(int minutes, int seconds)
        {
            return ( 4 * ( minutes >> 6 ) + ( seconds >> 6 ) );
        }

        public static TimeSpan DecodeTime(int hours, int minutes, int seconds)
        {
            TimeSpan timeSpan = new TimeSpan(0,
                BCDToDecimal(hours),
                BCDToDecimal(minutes),
                BCDToDecimal(seconds));

            return ( timeSpan );
        }
    }

    [Serializable]
    public class SessionDecoder
    {
        public SessionDecoder(List<int> theData)
        {
            m_theData = theData;
            RideInformation = new List<RideInformation>();
        }

        public SessionDecoder()
        {
        }

        public void Decode()
        {
            RiderWeight = m_theData[98];
            RiderHeight = m_theData[100];

            DateOfBirth = new DateTime(1900 + m_theData[103], m_theData[102], m_theData[101]);

            HRMaxUser = m_theData[105];
            HRSitUser = m_theData[107];

            // Rides info starts at 108 and are 44 bytes long
            int currentRide = 108;

            for ( int i = currentRide; i < m_theData.Count; i += 44 )
            {
                if ( ( m_theData.Count > i ) && m_theData[i] == 73 )
                {
                    RideInformation rideInformation = new RideInformation(m_theData, i);
                    RideInformation.Add(rideInformation);

                    if (rideInformation.NumberOfLaps > 0) {
                        i += rideInformation.NumberOfLaps * 12;
                    }
                }
            }
        }

        [XmlIgnore]
        private List<int> m_theData;

        public List<RideInformation> RideInformation { get; set; }
        public int RiderWeight { get; set; }
        public int RiderHeight { get; set; }
        public DateTime DateOfBirth { get; set; }
        public int HRMaxUser { get; set; }
        public int HRSitUser { get; set; }
    }

    [Serializable]
    public class RideInformation
    {
        public RideInformation()
        {
        }

        public RideInformation(List<int> m_theData, int rideOffset)
        {
            Distance = ((m_theData[rideOffset + 19] * 255.0) + m_theData[rideOffset + 18]) / 10.0;
            AverageSpeed = ((m_theData[rideOffset + 21] * 255.0) + m_theData[rideOffset + 20]) / 10.0;
            MaximumSpeed = ((m_theData[rideOffset + 23] * 255.0) + m_theData[rideOffset + 22]) / 10.0;
            AverageCadence = m_theData[rideOffset + 24];
            MaximumCadence = m_theData[rideOffset + 25];
            HRLimitMaximum = m_theData[rideOffset + 37];
            HRLimitMinimum = m_theData[rideOffset + 38];
            HRMaximum = m_theData[rideOffset + 6];
            HRAverage = m_theData[rideOffset + 5];
            NumberOfLaps = m_theData[rideOffset + 30];

            StartDateTime = DecoderHelpers.DecodeDateTime(m_theData[rideOffset + 33], m_theData[rideOffset + 32],
                                              m_theData[rideOffset + 36], m_theData[rideOffset + 35], m_theData[rideOffset + 34]);

            RideTime = DecoderHelpers.DecodeTime(m_theData[rideOffset + 29],
                                                 m_theData[rideOffset + 28],
                                                 m_theData[rideOffset + 27]);

            ExerciseTime = DecoderHelpers.DecodeTime(m_theData[rideOffset + 4],
                                                     m_theData[rideOffset + 3],
                                                     m_theData[rideOffset + 2]);
        }

        public double RideTimeSeconds
        {
            get { return RideTime.TotalSeconds; }
            set { RideTime = new TimeSpan(0, 0, (int) value); }
        }

        public double ExerciseTimeSeconds
        {
            get { return ExerciseTime.TotalSeconds; }
            set { ExerciseTime = new TimeSpan(0, 0, (int) value); }
        }

        public string[] ToListViewStrings()
        {
            string[] returnStrings = new string[12];

            returnStrings[0] = StartDateTime.ToLongDateString() + " " + StartDateTime.ToLongTimeString();
            returnStrings[1] = ExerciseTime.ToString();
            returnStrings[2] = RideTime.ToString();
            returnStrings[3] = Distance.ToString();
            returnStrings[4] = AverageSpeed.ToString();
            returnStrings[5] = MaximumSpeed.ToString();
            returnStrings[6] = AverageCadence.ToString();
            returnStrings[7] = MaximumCadence.ToString();
            returnStrings[8] = HRLimitMinimum.ToString();
            returnStrings[9] = HRLimitMaximum.ToString();
            returnStrings[10] = HRAverage.ToString();
            returnStrings[11] = HRMaximum.ToString();

            return ( returnStrings );
        }

        [XmlIgnore]
        public TimeSpan RideTime { get; set; }
        [XmlIgnore]
        public TimeSpan ExerciseTime { get; set; }
        public double Distance { get; set; }
        public double AverageSpeed { get; set; }
        public double MaximumSpeed { get; set; }
        public int AverageCadence { get; set; }
        public int MaximumCadence { get; set; }
        public DateTime StartDateTime { get; set; }
        public int HRLimitMaximum { get; set; }
        public int HRLimitMinimum { get; set; }
        public int HRAverage { get; set; }
        public int HRMaximum { get; set; }
        public int NumberOfLaps { get; set; }
    }
}
